package me.boot.gen.uid;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baidu.fsg.uid.UidGenerator;
import com.baidu.fsg.uid.buffer.RejectedPutBufferHandler;
import com.baidu.fsg.uid.buffer.RejectedTakeBufferHandler;
import com.baidu.fsg.uid.impl.CachedUidGenerator;
import com.baidu.fsg.uid.impl.DefaultUidGenerator;
import com.baidu.fsg.uid.impl.HuToolUidGenerator;
import com.baidu.fsg.uid.worker.entity.WorkerNode;
import com.baidu.fsg.uid.worker.service.DisposableWorkerIdAssigner;
import com.baidu.fsg.uid.worker.service.WorkerIdAssigner;
import lombok.extern.slf4j.Slf4j;
import me.boot.gen.table.StarupMode;
import me.boot.gen.table.TableGenerator;
import me.boot.gen.uid.condition.CacheIdCondition;
import me.boot.gen.uid.condition.DefaultIdCondition;
import me.boot.gen.uid.condition.HutoolIdCondition;
import me.boot.gen.uid.condition.WorkerIdAssignerCondition;
import me.boot.gen.uid.propertites.CacheId;
import me.boot.gen.uid.propertites.HutoolId;
import me.boot.gen.uid.propertites.UidProperties;
import me.boot.gen.uid.propertites.UidType;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;


@Slf4j
@ComponentScan({"me.boot.gen.uid", "com.baidu.fsg.uid"})
@MapperScan({"me.boot.gen.table.mapper", "com.baidu.fsg.uid.worker.mapper"})
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(UidProperties.class)
public class UidConfiguration {

    @Autowired
    UidProperties properties;

    @PostConstruct
    public void init() {
        log.info("Init UidConfiguration");
    }

    @Bean
    @ConditionalOnMissingBean
    @Conditional(HutoolIdCondition.class)
    public UidGenerator huToolUidGenerator() {
        HutoolId id = properties.getHutoolId();
        return new HuToolUidGenerator(id.getWorkerId(), id.getDataCenterId());
    }

    @Bean
    @ConditionalOnMissingBean
    @Conditional(DefaultIdCondition.class)
    public UidGenerator defaultUidGenerator(WorkerIdAssigner workerIdAssigner) {
        DefaultUidGenerator uidGenerator = new DefaultUidGenerator();
        BeanUtil.copyProperties(properties.getDefaultId(), uidGenerator);
        uidGenerator.setWorkerIdAssigner(workerIdAssigner);
        return uidGenerator;
    }

    @Bean
    @ConditionalOnMissingBean
    @Conditional(CacheIdCondition.class)
    public UidGenerator cacheUidGenerator(WorkerIdAssigner workerIdAssigner) {
        CachedUidGenerator uidGenerator = new CachedUidGenerator();
        CacheId cacheId = properties.getCacheId();
        BeanUtil.copyProperties(cacheId, uidGenerator);
        if (cacheId.getRejectedPutBufferHandlerClass() != null) {
            RejectedPutBufferHandler rejectedPutBufferHandler = ReflectUtil.newInstance(cacheId.getRejectedPutBufferHandlerClass());
            uidGenerator.setRejectedPutBufferHandler(rejectedPutBufferHandler);
        }
        if (cacheId.getRejectedTakeBufferHandlerClass() != null) {
            RejectedTakeBufferHandler rejectedTakeBufferHandler = ReflectUtil.newInstance(cacheId.getRejectedTakeBufferHandlerClass());
            uidGenerator.setRejectedTakeBufferHandler(rejectedTakeBufferHandler);
        }
        uidGenerator.setWorkerIdAssigner(workerIdAssigner);
        return uidGenerator;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty("spring.datasource.url")
    @Conditional(WorkerIdAssignerCondition.class)
    public WorkerIdAssigner workerIdAssigner() {
        autoCreateTable();
        return new DisposableWorkerIdAssigner();
    }

    private void autoCreateTable() {
        if (StrUtil.equalsAnyIgnoreCase(properties.getType().name(),
                UidType.Default.name(), UidType.Cache.name())) {
            if (!TableGenerator.existTable(WorkerNode.TABLE_NAME)) {
                if (properties.isAutoCreateTable()) {
                    TableGenerator.generateTable(StarupMode.Create, WorkerNode.class);
                } else {
                    log.error(StrUtil.format("未开启自动建表，请提前手工建表: {}", WorkerNode.TABLE_NAME));
                }
            }
        }
    }
}
